﻿using System.Runtime.InteropServices;

namespace Godot.core.os
{
    public enum MouseButton
    {
        BUTTON_LEFT = 1,
        BUTTON_RIGHT = 2,
        BUTTON_MIDDLE = 3,
        BUTTON_WHEEL_UP = 4,
        BUTTON_WHEEL_DOWN = 5,
        BUTTON_MASK_LEFT = (1 << (BUTTON_LEFT - 1)),
        BUTTON_MASK_RIGHT = (1 << (BUTTON_RIGHT - 1)),
        BUTTON_MASK_MIDDLE = (1 << (BUTTON_MIDDLE - 1))
    }

    public enum JoystickButton
    {
        JOY_BUTTON_0 = 0,
        JOY_BUTTON_1 = 1,
        JOY_BUTTON_2 = 2,
        JOY_BUTTON_3 = 3,
        JOY_BUTTON_4 = 4,
        JOY_BUTTON_5 = 5,
        JOY_BUTTON_6 = 6,
        JOY_BUTTON_7 = 7,
        JOY_BUTTON_8 = 8,
        JOY_BUTTON_9 = 9,
        JOY_BUTTON_10 = 10,
        JOY_BUTTON_11 = 11,
        JOY_BUTTON_12 = 12,
        JOY_BUTTON_13 = 13,
        JOY_BUTTON_14 = 14,
        JOY_BUTTON_15 = 15,
        JOY_BUTTON_MAX = 16,

        JOY_L = JOY_BUTTON_4,
        JOY_R = JOY_BUTTON_5,
        JOY_L2 = JOY_BUTTON_6,
        JOY_R2 = JOY_BUTTON_7,
        JOY_L3 = JOY_BUTTON_8,
        JOY_R3 = JOY_BUTTON_9,
        JOY_SELECT = JOY_BUTTON_10,
        JOY_START = JOY_BUTTON_11,
        JOY_DPAD_UP = JOY_BUTTON_12,
        JOY_DPAD_DOWN = JOY_BUTTON_13,
        JOY_DPAD_LEFT = JOY_BUTTON_14,
        JOY_DPAD_RIGHT = JOY_BUTTON_15,

        // a little history about game controllers (who copied who)

        JOY_SNES_B = JOY_BUTTON_0,
        JOY_SNES_A = JOY_BUTTON_1,
        JOY_SNES_Y = JOY_BUTTON_2,
        JOY_SNES_X = JOY_BUTTON_3,

        JOY_SONY_CIRCLE = JOY_SNES_A,
        JOY_SONY_X = JOY_SNES_B,
        JOY_SONY_SQUARE = JOY_SNES_Y,
        JOY_SONY_TRIANGLE = JOY_SNES_X,

        JOY_SEGA_B = JOY_SNES_A,
        JOY_SEGA_A = JOY_SNES_B,
        JOY_SEGA_X = JOY_SNES_Y,
        JOY_SEGA_Y = JOY_SNES_X,

        JOY_XBOX_B = JOY_SEGA_B,
        JOY_XBOX_A = JOY_SEGA_A,
        JOY_XBOX_X = JOY_SEGA_X,
        JOY_XBOX_Y = JOY_SEGA_Y,

        JOY_DS_A = JOY_SNES_A,
        JOY_DS_B = JOY_SNES_B,
        JOY_DS_X = JOY_SNES_X,
        JOY_DS_Y = JOY_SNES_Y,

        JOY_WII_C = JOY_BUTTON_5,
        JOY_WII_Z = JOY_BUTTON_6,

        JOY_WII_MINUS = JOY_BUTTON_9,
        JOY_WII_PLUS = JOY_BUTTON_10,

        // end of history

        JOY_AXIS_0 = 0,
        JOY_AXIS_1 = 1,
        JOY_AXIS_2 = 2,
        JOY_AXIS_3 = 3,
        JOY_AXIS_4 = 4,
        JOY_AXIS_5 = 5,
        JOY_AXIS_6 = 6,
        JOY_AXIS_7 = 7,
        JOY_AXIS_MAX = 8,

        JOY_ANALOG_0_X = JOY_AXIS_0,
        JOY_ANALOG_0_Y = JOY_AXIS_1,

        JOY_ANALOG_1_X = JOY_AXIS_2,
        JOY_ANALOG_1_Y = JOY_AXIS_3,

        JOY_ANALOG_2_X = JOY_AXIS_4,
        JOY_ANALOG_2_Y = JOY_AXIS_5,
    }

    [StructLayout(LayoutKind.Explicit)]
    public struct InputModifierState 
    {
	    [FieldOffset(0)] public bool shift;
	    [FieldOffset(1)] public bool alt;

	    [FieldOffset(2)] public bool command; //< windows/mac key
	    [FieldOffset(2)] public bool control;
	    
	    [FieldOffset(3)] public bool meta; //< windows/mac key

	    public static bool operator == ( InputModifierState lvalue, InputModifierState rvalue) 
        {
		    return ((lvalue.shift == rvalue.shift) && (lvalue.alt == rvalue.alt)
                && (lvalue.control == rvalue.control)
                && (lvalue.meta == rvalue.meta));
	    }

        public static bool operator != (InputModifierState lvalue, InputModifierState rvalue)
        {
            return !(lvalue == rvalue);
        }
    };

    interface IInputEvent {}

    public class InputEventKey : IInputEvent
    {
	    public InputModifierState mod;
		
	    public bool pressed; /// otherwise release

	    public uint scancode; ///< check keyboard.h , KeyCode enum, without modifier masks
	    public uint unicode; ///unicode

	    public uint get_scancode_with_modifiers()
        {
            return 0;
        }

	    bool echo; /// true if this is an echo key
    }

    public class InputEventMouse  : IInputEvent
    {
	    public InputModifierState mod;
	    public int button_mask;
	    public int x,y;
	    public int global_x,global_y;
	    public int pointer_index;
    }

    public class InputEventMouseButton : InputEventMouse 
    {
	    public int button_index;
	    public bool pressed; //otherwise released
	    public bool doubleclick; //last even less than doubleclick time	
    }

    public class InputEventMouseMotion : InputEventMouse 
    {
	    public int relative_x,relative_y;
	    public float speed_x,speed_y;
    }

    public class InputEventJoystickMotion : IInputEvent 
    {
	    public int axis; ///< Joystick axis
	    public float axis_value; ///< -1 to 1
    }

    public class InputEventJoystickButton : IInputEvent 
    {
	    public int button_index;
	    public bool pressed;
	    public float pressure; //0 to 1
    }

    public class InputEventScreenTouch : IInputEvent 
    {
	    public int index;
	    public int x,y;
	    public bool pressed;
    }
    
    public class InputEventScreenDrag : IInputEvent 
    {
	    public int index;
	    public int x,y;
	    public int relative_x,relative_y;
	    public float speed_x,speed_y;
    }

    public class InputEventAction : IInputEvent 
    {
	    public int action;
	    public bool pressed;
    }

    public class InputEvent
    {
        public enum Type
        {
            NONE,
            KEY,
            MOUSE_MOTION,
            MOUSE_BUTTON,
            JOYSTICK_MOTION,
            JOYSTICK_BUTTON,
            SCREEN_TOUCH,
            SCREEN_DRAG,
            ACTION,
            TYPE_MAX
        };

        public uint ID;

        public int device;

        private InputEvent.Type __type;

        public InputEvent.Type type
        {
            get { return __type; }
            set
            {
                if (value == InputEvent.Type.KEY)
                    __event_obj = new InputEventKey();
                else if (value == InputEvent.Type.MOUSE_BUTTON)
                    __event_obj = new InputEventMouseButton();
                else if (value == InputEvent.Type.MOUSE_MOTION)
                    __event_obj = new InputEventMouseMotion();
                else if (value == InputEvent.Type.JOYSTICK_BUTTON)
                    __event_obj = new InputEventJoystickButton();
                else if (value == InputEvent.Type.JOYSTICK_MOTION)
                    __event_obj = new InputEventJoystickMotion();
                else if (value == InputEvent.Type.ACTION)
                    __event_obj = new InputEventAction();
                else if (value == InputEvent.Type.SCREEN_TOUCH)
                    __event_obj = new InputEventScreenTouch();
                else if (value == InputEvent.Type.SCREEN_DRAG)
                    __event_obj = new InputEventScreenDrag();
                else
                    __event_obj = null;
                __type = value;
            }
        }

        private IInputEvent __event_obj;

        public InputEventMouseMotion mouse_motion
        {
            get { return __event_obj as InputEventMouseMotion; }
            set { __event_obj = value; }
        }

        public InputEventMouseButton mouse_button
        {
            get { return __event_obj as InputEventMouseButton; }
            set { __event_obj = value; }
        }

		public InputEventJoystickMotion joy_motion
        {
            get { return __event_obj as InputEventJoystickMotion; }
            set { __event_obj = value; }
        }

		public InputEventJoystickButton joy_button
        {
            get { return __event_obj as InputEventJoystickButton; }
            set { __event_obj = value; }
        }

		public InputEventKey key
        {
            get { return __event_obj as InputEventKey; }
            set { __event_obj = value; }
        }

        public InputEventScreenTouch screen_touch
        {
            get { return __event_obj as InputEventScreenTouch; }
            set { __event_obj = value; }
        }

		public InputEventScreenDrag screen_drag
        {
            get { return __event_obj as InputEventScreenDrag; }
            set { __event_obj = value; }
        }

		public InputEventAction action
        {
            get { return __event_obj as InputEventAction; }
            set { __event_obj = value; }
        }

        public virtual bool is_pressed()
        {
            return false;
        }

        public virtual bool is_action(string p_action)
        {
            return false;
        }

        public virtual bool is_echo()
        {
            return false;
        }

        public virtual void set_as_action(string p_action)
        {

        }

        public static bool operator == (InputEvent p_evt1, InputEvent p_evt2)
        {
            return true;
        }

        public static bool operator != (InputEvent p_evt1, InputEvent p_evt2)
        {
            return false;
        }

        public static implicit operator string(InputEvent p_evt)
        {
            return null;
        }

        public InputEvent() { }
    }
}
